from uuid import uuid4
from flask import Flask
from flask_restful import Resource, Api
from flask_restful import reqparse
# from common.utils import byteify
from blockchain import Blockchain

MESSAGE = "message"
CHAIN = "chain"
LENGTH = "length"
TOTAL_NODES = "total_nodes"
NEW_CHAIN = "new_chain"

INDEX = "index"
TRANSACTIONS = "transactions"
PROOF = "proof"
PREVIOUS_HASH = "previous_hash"

app = Flask("InnerVoice")
api = Api(app)

# Generate a globally unique address for this node
node_identifier = str(uuid4()).replace('-', '')

class InnerVoice(Resource):

    def _mine(self):

        # We run the proof of work algorithm to
        # get the next proof...
        blk = Blockchain()
        last_block = blk.last_block
        last_proof = last_block["proof"]
        proof = blk.proof_of_work(last_proof)

        # 给工作量证明的节点提供奖励.
        # 发送者为 "0" 表明是新挖出的币
        blk.new_transaction (
                sender="0",
                recipient=node_identifier,
                amount=1,
            )

        # Forge the new Block by adding it to the chain
        block = blk.new_block(proof)

        return {
                MESSAGE: "New Block Forged",
                INDEX: block[INDEX],
                TRANSACTIONS: block[TRANSACTIONS],
                PROOF: block[PROOF],
                PREVIOUS_HASH: block[PREVIOUS_HASH]
            }, 200

    def get(self, todo_elem):
        # 
        blk = Blockchain()
        if todo_elem.lower() == "mine":
            return self._mine()

        elif todo_elem.lower() == "chain":
            return { 
                    MESSAGE: "We'll get chain.",
                    CHAIN: blk.chain,
                    LENGTH: len(blk.chain)
                }, 200

class Transaction(Resource):

    def post(self):
        # 
        values = trans_req_arg_parser()

        required = [ "sender", "recipient", "amount" ]
        if not all(k in values for k in required):
            return { MESSAGE: "Missing values"}, 400

        # Create a new Transaction
        blk = Blockchain()
        index = blk.new_transaction(values["sender"],
                                     values["recipient"],
                                     values["amount"])

        return { MESSAGE: f"Transaction will be added to Block {index}" }, 201

class NodesRegister(Resource):

    def post(self):

        values = nodes_register_arg_parser()

        nodes = eval(values["nodes"])
        if nodes is None:
            return { MESSAGE: f"Please supply a valid list of nodes" }, 400

        blk = Blockchain()
        for node in nodes:
            print("Register node %s" % (node))
            blk.register_node(node)

        response = {
            MESSAGE: f"New nodes have been added",
            TOTAL_NODES: list(blk.nodes)
        }

        return response, 201

class NodesResolve(Resource):

    def get(self):

        blk = Blockchain()
        replaced = blk.resolve_conflicts()

        if replaced:
            response = {
                MESSAGE: "Our chain was replaced",
                NEW_CHAIN: blk.chain
            }
        else:
            response = {
                MESSAGE: "Our chain is authouritative",
                CHAIN: blk.chain
            }

        return response, 200

def trans_req_arg_parser():

    parser = reqparse.RequestParser()
    parser.add_argument("sender", type=str)
    parser.add_argument("recipient", type=str)
    parser.add_argument("amount", type=int)
    # return byteify(parser.parse_args())
    return parser.parse_args()

def nodes_register_arg_parser():

    parser = reqparse.RequestParser()
    parser.add_argument("nodes", type=str)

    return parser.parse_args()

api.add_resource(InnerVoice, '/innervoice/<string:todo_elem>')
api.add_resource(Transaction, '/transactions/new')
api.add_resource(NodesRegister, '/nodes/register')
api.add_resource(NodesResolve, '/nodes/resolve')

if __name__ == '__main__':
    blk = Blockchain()
    app.run(debug=True)
